# coding: utf-8

import socket
from socket import AF_INET
from socket import SOCK_STREAM
import os
from time import time, strftime, gmtime#, ctime, sleep
from optparse import OptionParser

import config

class Server():
    
    def __init__(self):
        pass
    
    def optionHandler(self): # -d d:\www | --documentRoot=d:\www
        parser = OptionParser()
        
        parser.set_defaults(DOCUMENT_ROOT = "d:\www") # default document root
        
        parser.add_option( \
                          "-d", "--documentRoot", dest="DOCUMENT_ROOT", \
                          help="Set document root directory for server" \
                          )
        (options, args) = parser.parse_args()
        
        if not os.path.isdir(options.DOCUMENT_ROOT):
            print "Service failed to start T_T"
            print "Server's document root directory is invalid!"
            return False
        else:
            self.DOCUMENT_ROOT = options.DOCUMENT_ROOT
            return True
    
    def mimeHandler(self, requestURI):
        fileName = requestURI.split("/")[-1]
        fileExt = fileName.split(".")[-1]
        if fileExt in config.mimeType:
            return config.mimeType[fileExt]
        else:
            return "text/plain"
    
    def timeHandler(self, timestamp):
        return strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime(timestamp))
    
    def requestToResponseAttrHandler(self, dataFromClient):
        dataList = dataFromClient.split(config.crlf)
        #print dataList
        
        requestLine = dataList[0]
        if requestLine.find("HTTP/") != -1:
            requestLineList = requestLine.split(" ")
            if len(requestLineList) == 3:
                (method, requestURI, httpVersion) = requestLineList
            else:
                print "Invalid request line"
                return 400
            
            requestFilePath = self.DOCUMENT_ROOT + requestURI.replace(config.uriSlash, config.pathSlash)
            if os.path.isfile(requestFilePath):
                if method in config.serverSupportRequestMethod:
                    if method == "GET":
                        # Status Line
                        statusCode = 200
                        reasonPhrase = config.statusCodeReasonPhrase[statusCode]
                        
                        # Entity
                        # Entity Header Fields
                        # Entity Body
                        lastModified = self.timeHandler(os.path.getmtime(requestFilePath))
                        contentLength = os.path.getsize(requestFilePath)
                        contentType = self.mimeHandler(requestURI)
                        
                        """
                        openMode = "rt"
                        if contentType.split("/")[0] != "text":
                            openMode = "rb"
                        #print openMode
                        """
                        
                        contentData = open(requestFilePath, "rb")
                        
                        responseAttr = (httpVersion, statusCode, reasonPhrase, lastModified, contentLength, contentType, contentData)
                        return responseAttr
                else:
                    print "Unknown request method"
                    return 400
            else:
                #print "Request url not found"
                return 404
        else:
            print "Unknown request version"
            return 400
    
    def responseHandler(self, responseData):
        import types
        if type(responseData) is types.IntType:
            statusCode = responseData
            reasonPhrase = config.statusCodeReasonPhrase[statusCode]
            
            contentData = open(self.DOCUMENT_ROOT + "\error.tpl").read() \
                % (statusCode, reasonPhrase, statusCode, reasonPhrase, config.serverVersion)
            
            httpResponse = \
                config.serverSupportRequestVersion + " " + str(statusCode) + " " + reasonPhrase + config.crlf + \
                "Date:" + self.timeHandler(time()) + config.crlf + \
                "Server:" + config.serverVersion + config.crlf + \
                "Content-Length:" + str(len(contentData)) + config.crlf + \
                "Content-Type:text/html" + config.crlf + config.crlf + \
                contentData
        else:
            httpResponse = \
                config.serverSupportRequestVersion + " " + str(responseData[1]) + " " + responseData[2] + config.crlf + \
                "Date:" + self.timeHandler(time()) + config.crlf + \
                "Server:" + config.serverVersion + config.crlf + \
                "Last-Modified:" + responseData[3] + config.crlf + \
                "Accept-Ranges: bytes" + config.crlf + \
                "Content-Length:" + str(responseData[4]) + config.crlf + \
                "Content-Type:" + responseData[5] + config.crlf + config.crlf \
                
            httpResponse += responseData[6].read()
            responseData[6].close()
        return httpResponse
    
    def connectedSocketThreadAllocate(self, connectedSocket):
        #print 'start loop at:', ctime()
        while True:
            data = connectedSocket.recv(1024)
            if not data: break
            
            if self.requestToResponseAttrHandler(data):
                connectedSocket.send(self.responseHandler(self.requestToResponseAttrHandler(data)))
                #sleep(3)
        connectedSocket.close()
        #print 'loop done at:', ctime()
        
    def startServerSocket(self, runningMode = ("singleThread",)):
        serverSocket = socket.socket(AF_INET, SOCK_STREAM)
        serverSocket.bind(config.serverAddress)
        serverSocket.listen(config.serverBacklog)
        print "Service starts successfully (^o^)"
        
        if runningMode[0] == "multiThread":
            from multiprocessing.pool import ThreadPool
            pool = ThreadPool(processes = runningMode[1])
        
        while True:
            connectedSocket, address = serverSocket.accept()
            (clientHost, clientPort) = address
            print "Client from Host: " + clientHost + " Port: " + str(clientPort) + " connected"
            
            if runningMode[0] == "singleThread":
                self.connectedSocketThreadAllocate(connectedSocket)
            elif runningMode[0] == "multiThread":
                """ without thread pool
                import thread
                thread.start_new_thread(self.connectedSocketThreadAllocate, (connectedSocket,))
                """
                
                pool.apply_async(self.connectedSocketThreadAllocate, (connectedSocket,))
        
        serverSocket.close()
            
if __name__ == "__main__":
    server = Server()
    if server.optionHandler():
        server.startServerSocket()
